﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Security;

using LeanEngine.Business.Security.Entity;

namespace LeanEngine.Business.Security
{
    internal sealed class SecurityMessageSink : IMessageSink
    {
        #region Declaration Members

        private IMessageSink _nextSink;

        #endregion

        #region Constructor Members

        public SecurityMessageSink(IMessageSink nextSink)
        {
            this._nextSink = nextSink;
        }

        #endregion

        #region IMessageSink Members

        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return this.NextSink.AsyncProcessMessage(msg, replySink);
        }

        public IMessageSink NextSink
        {
            get { return _nextSink; }
        }

        public IMessage SyncProcessMessage(IMessage msg)
        {
            IMethodMessage methodMessage = (IMethodMessage)msg;

            if (!methodMessage.MethodName.Contains(".ct"))
            {
                var secureBehaviour = methodMessage.MethodBase.GetCustomAttributes(typeof(SecureBehaviorAttribute), false).FirstOrDefault() as SecureBehaviorAttribute;
                if (secureBehaviour != null)
                {
                    var token = Thread.CurrentPrincipal.Identity as Token;

                    if (token == null || !Thread.CurrentPrincipal.Identity.IsAuthenticated)
                    {
                        throw new SecurityException(Resource.ErrorMessages.LoginRequired);
                    }
                    
                    if (!secureBehaviour.ValidatorBehavior.ValidateAccess(token.UserName, token.Password, methodMessage.MethodName))
                    {
                        throw new SecurityException(Resource.ErrorMessages.AccessDenied);
                    }
                }
            }

            var returnMessage = (IMethodReturnMessage)this.NextSink.SyncProcessMessage(msg);

            return returnMessage;
        }

        #endregion
    }
}
